/*
    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

*/


#ifndef FLOATMATRIX_H
#define FLOATMATRIX_H

#include <QVector>
#include "objects/vertex.h"

#include <iostream>
using namespace std;


using namespace object;

namespace draw{
  
class FloatMatrix
{
  public:
    FloatMatrix(){resetMatrix();}
    ~FloatMatrix(){;}
    inline void resetMatrix(){
      m_a0 = 0;
      m_a1= 0;
      m_a2= 0;
      m_a3= 0;
      
      m_b0= 0;
      m_b1= 0;
      m_b2= 0;
      m_b3= 0;
      
      m_c0= 0;
      m_c1= 0;
      m_c2= 0;
      m_c3= 0;
      
      m_d0= 0;
      m_d1= 0;
      m_d2= 0;
      m_d3= 0;
    }
    inline void setIdentity(){
      m_a0 = 1;
      m_a1= 0;
      m_a2= 0;
      m_a3= 0;
      
      m_b0= 0;
      m_b1= 1;
      m_b2= 0;
      m_b3= 0;
      
      m_c0= 0;
      m_c1= 0;
      m_c2= 1;
      m_c3= 0;
      
      m_d0= 0;
      m_d1= 0;
      m_d2= 0;
      m_d3= 1;
    }    
    /*
    * letter = row, number = col
    */
    inline float a0(){return m_a0;}
    inline float a1(){return m_a1;}
    inline float a2(){return m_a2;}
    inline float a3(){return m_a3;}
    
    inline float b0(){return m_b0;}
    inline float b1(){return m_b1;}
    inline float b2(){return m_b2;}
    inline float b3(){return m_b3;}
    
    inline float c0(){return m_c0;}
    inline float c1(){return m_c1;}
    inline float c2(){return m_c2;}
    inline float c3(){return m_c3;}
    
    inline float d0(){return m_d0;}
    inline float d1(){return m_d1;}
    inline float d2(){return m_d2;}
    inline float d3(){return m_d3;}
    
    inline void seta0(float val){m_a0 = val;}
    inline void seta1(float val){m_a1= val;}
    inline void seta2(float val){m_a2= val;}
    inline void seta3(float val){m_a3= val;}
    
    inline void setb0(float val){m_b0= val;}
    inline void setb1(float val){m_b1= val;}
    inline void setb2(float val){m_b2= val;}
    inline void setb3(float val){m_b3= val;}
    
    inline void setc0(float val){m_c0= val;}
    inline void setc1(float val){m_c1= val;}
    inline void setc2(float val){m_c2= val;}
    inline void setc3(float val){m_c3= val;}
    
    inline void setd0(float val){m_d0= val;}
    inline void setd1(float val){m_d1= val;}
    inline void setd2(float val){m_d2= val;}
    inline void setd3(float val){m_d3= val;}
  
    
    inline FloatMatrix* multiplyWith(FloatMatrix *temp){  
        FloatMatrix *ret = new FloatMatrix();
        /*qDebug("Rows: " + QString::number(m_rows).toLatin1() + 
                "Cols: " + QString::number(temp->cols()).toLatin1());*/
        
        ret->seta0( m_a0*temp->a0()+
                    m_a1*temp->b0() +
                    m_a2*temp->c0() +
                    m_a3*temp->d0());
        ret->seta1( m_a0*temp->a1()+
                    m_a1*temp->b1() +
                    m_a2*temp->c1() +
                    m_b3*temp->d1());
        ret->seta2( m_a0*temp->a2()+
                    m_a1*temp->b2() +
                    m_a2*temp->c2() +
                    m_a3*temp->d2());
        ret->seta3( m_a0*temp->a3()+
                    m_a1*temp->b3() +
                    m_a2*temp->c3() +
                    m_a3*temp->d3());
                    
        ret->setb0( m_b0*temp->a0()+
                    m_b1*temp->b0() +
                    m_b2*temp->c0() +
                    m_b3*temp->d0());
        ret->setb1( m_b0*temp->a1()+
                    m_b1*temp->b1() +
                    m_b2*temp->c1() +
                    m_b3*temp->d1());
        ret->setb2( m_b0*temp->a2()+
                    m_b1*temp->b2() +
                    m_b2*temp->c2() +
                    m_b3*temp->d2());
        ret->setb3( m_b0*temp->a3()+
                    m_b1*temp->b3() +
                    m_b2*temp->c3() +
                    m_b3*temp->d3());
                    
        ret->setc0( m_c0*temp->a0()+
                    m_c1*temp->b0() +
                    m_c2*temp->c0() +
                    m_c3*temp->d0());
        ret->setc1( m_c0*temp->a1()+
                    m_c1*temp->b1() +
                    m_c2*temp->c1() +
                    m_c3*temp->d1());
        ret->setc2( m_c0*temp->a2()+
                    m_c1*temp->b2() +
                    m_c2*temp->c2() +
                    m_c3*temp->d2());
        ret->setc3( m_c0*temp->a3()+
                    m_c1*temp->b3() +
                    m_c2*temp->c3() +
                    m_c3*temp->d3());
                    
        ret->setd0( m_d0*temp->a0()+
                    m_d1*temp->b0() +
                    m_d2*temp->c0() +
                    m_d3*temp->d0());
        ret->setd1( m_d0*temp->a1()+
                    m_d1*temp->b1() +
                    m_d2*temp->c1() +
                    m_d3*temp->d1());
        ret->setd2( m_d0*temp->a2()+
                    m_d1*temp->b2() +
                    m_d2*temp->c2() +
                    m_d3*temp->d2());
        ret->setd3( m_d0*temp->a3()+
                    m_d1*temp->b3() +
                    m_d2*temp->c3() +
                    m_d3*temp->d3());
    
        return ret;
    }
    
    // 4X4 matrix X VERTEX!
    inline Vertex* multiplyWith(Vertex *temp){
      Vertex * ret = new Vertex();
      
      ret->setX(m_a0*temp->x()+m_a1*temp->y()+m_a2*temp->z()+m_a3*temp->h());
      ret->setY(m_b0*temp->x()+m_b1*temp->y()+m_b2*temp->z()+m_b3*temp->h());
      ret->setZ(m_c0*temp->x()+m_c1*temp->y()+m_c2*temp->z()+m_c3*temp->h());
      ret->setH(m_d0*temp->x()+m_d1*temp->y()+m_d2*temp->z()+m_d3*temp->h());

      return ret;
    }
    
    FloatMatrix* inverseTranspone(){
      
      FloatMatrix *ret = new FloatMatrix();
        float mainDet = 1.0/determinant();
        float factor1 = (m_b1*m_c2-m_b2*m_c1);
        float factor2 = (m_b0*m_c2-m_b2*m_c0);
        float factor3 = (m_b0*m_c1-m_b1*m_c0);
        
        float factor4 = (m_a1*m_c2-m_a2*m_c1);
        float factor5 = (m_a0*m_c2-m_a2*m_c0);
        float factor6 = (m_a0*m_c1-m_a1*m_c0);
        
        float factor7 = (m_a1*m_b2-m_a2*m_b1);
        float factor8 = (m_a0*m_b2-m_a2*m_b0);
        float factor9 = (m_a0*m_b1-m_a1*m_b0);
        
        ret->seta0(factor1*mainDet);
        ret->seta1(-factor2*mainDet);
        ret->seta2(factor3*mainDet);
        
        ret->setb0(-factor4*mainDet);
        ret->setb1(factor5*mainDet);
        ret->setb2(-factor6*mainDet);
        
        ret->setc0(factor7*mainDet);
        ret->setc1(-factor8*mainDet);
        ret->setc2(factor9*mainDet);
        
        return ret;
    }
    
    inline float determinant()
    {
      float factor1 = m_a0*(m_b1*m_c2-m_b2*m_c1);
      float factor2 = m_a1*(m_b0*m_c2-m_b2*m_c0);
      float factor3 = m_a2*(m_b0*m_c1-m_b1*m_c0);
      
      float total = factor1 - factor2 + factor3;
      return total;
    }
    
    inline void print()
    {
      cout << "R0: ";
      cout <<  m_a0 << " ";
      cout <<  m_a1 << " ";
      cout <<  m_a2 << " ";
      cout <<  m_a3 << "\n";
      
      cout << "R1: ";
      cout <<  m_b0<< " ";
      cout <<  m_b1<< " ";
      cout <<  m_b2<< " ";
      cout <<  m_b3<<  "\n";
      
      cout << "R2: ";
      cout <<  m_c0<< " ";
      cout <<  m_c1<< " ";
      cout <<  m_c2<< " ";
      cout <<  m_c3<<  "\n";
      
      cout << "R3: ";
      cout <<  m_d0<< " ";
      cout <<  m_d1<< " ";
      cout <<  m_d2<< " ";
      cout <<  m_d3<<  "\n\n";
    }
  
  private:
    float m_a0;
    float m_a1;
    float m_a2;
    float m_a3;
    
    float m_b0;
    float m_b1;
    float m_b2;
    float m_b3;
    
    float m_c0;
    float m_c1;
    float m_c2;
    float m_c3;
    
    float m_d0;
    float m_d1;
    float m_d2;
    float m_d3;
};

};

#endif // FLOATMATRIX_H
